package com.chrray.position.util.jwt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.chrray.position.util.result.ResponseResult;
import com.chrray.position.util.result.ResponseResults;
import com.chrray.position.util.result.ResultEnum;
import com.chrray.position.util.rsa.RSAUtil;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 前端JWT管理
 * @Author chen
 * @Date 2020/3/20
 * @Param
 * @return
 */
@Component
public class JWTUtilForFrontEnd {

    /**
     * 过期时间30天
     */
    private static final long EXPIRE_TIME = 30 * 24 * 60 * 60 * 1000L;

    /**
     * 秘钥
     */
    private static final String SECRET = "0763c60f-4fb7-4aa4-90e9-d2731b31bb83";
    /**
     * 公钥
     */
    private static final String publickey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA" +
            "onWzmO5C887p8jdTezrrOXwkeVqX2vqbjHtXmjkKstTcgezL6tXIWhult12kXykh3NR+YoBPh/j2Z5sbFC5degqXyPV2aMXLRlpmFpvBZN5D" +
            "SEW/GWDwZyjLwfj/00iB0QZdq1o0rJg8M/enTUfZn7OcekY5aIncoPSprx+aW3eTpvfjfIid0Nri" +
            "ToAXvclrd96rRuNCy/IUeNkUwg8U1XzNcgcxT93Lb7r6tM923hHIiGe6TNqzWoT/mS/2F29VMUxS" +
            "/dR35btofOVcOFzgt/9LXpTzHZpLbw9a+AQBbhvL0z3GboFJx0l3dcvyx/SBOR+wiJ7JunMkGbRK" +
            "cjHNnQIDAQAB";
    /**
     * 私钥
     */
    private static final String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCidbOY7kLzzunyN1N7Ous5fCR5" +
            "Wpfa+puMe1eaOQqy1NyB7Mvq1chaG6W3XaRfKSHc1H5igE+H+PZnmxsULl16CpfI9XZoxctGWmYW" +
            "m8Fk3kNIRb8ZYPBnKMvB+P/TSIHRBl2rWjSsmDwz96dNR9mfs5x6Rjloidyg9KmvH5pbd5Om9+N8" +
            "iJ3Q2uJOgBe9yWt33qtG40LL8hR42RTCDxTVfM1yBzFP3ctvuvq0z3beEciIZ7pM2rNahP+ZL/YX" +
            "b1UxTFL91Hflu2h85Vw4XOC3/0telPMdmktvD1r4BAFuG8vTPcZugUnHSXd1y/LH9IE5H7CInsm6" +
            "cyQZtEpyMc2dAgMBAAECggEAeqI8u0GH2sEjigrHgqs8Kv2SDuUa4lrwlj/5JdrPP6U8+vqEB7Xu" +
            "8k4SbvNKA+dpv2fMK5DN+EcvwY+tBjPlrh1W7ISWKIq+YlIU5BYZC4pX4bN8iKzLbCO4g6jxfE0s" +
            "aVvT02hZgLu+HkBg0w5csZgEk6x043vFxt5JRH+npkjOjDp4zQ2cZnQJPXc2jT9baCnOWDCGU+6c" +
            "6+tESgEVMWesp3K0PDP4r6oRIipqCh9rWKUWIGwrv8YEQ0rZVAPZj3Oa2FW5sVOMadaTNSL+tkIl" +
            "Eer7CVEJmFzV0sp2Gw8GuQFVScGusvH8EkUxsQ+h9HYs8QUn5MnEEFlLQCjSRQKBgQDOZo5Dy2O1" +
            "ucFw8mcblII6BJxyZxNdPF1Qtrod/rq75SM2BS4rPVgZ7jIaDbOmO9yIw2ALLqk84SWLnfIGauZf" +
            "zyFelgriKKxpoO3MQ1aQb0jNvNl/+qvJm9V8ysnp9MQ8KjL9C7nm0O40N53CL0BuTyq8lTbJzP80" +
            "EjjV/iDuQwKBgQDJf/lTkEL9gMVK6fip1sW6WHPPJNFrrCa1eSVKo5yMtwJe7TiXtgHLduQ5J8FY" +
            "AehNpzX71LnLu8q/ANnNN0zV14EeL8iKGvv0LTNHWLcUIaZALRaD5VthDngyXywQQKWU/Xps2GlR" +
            "UC0ejXdwR+M/LvIoDzxMpg8ClIIAxlLGnwKBgB3VgyDXiF0fMY/i6fZ5m+1I91guAAWNESv2jaaC" +
            "SHmaYGGRlB/Z6R8uZ/2OF3ai1njKTNqw7vOG08d/mBlHYG9e4ELsyzvvhyRph5Xr8dpEy7A5aPLv" +
            "x98HcG9uXIs9vxO3QdGD3WY4swXHOD12WQjY1qOVqeq3f/gF3kNITXhDAoGAKhm4AVKp/Id0MWPT" +
            "VcEPDRFAGS6rJuEQMWh/dynaIrh2IVlVMrWPiHBt3NdGmVnErLzcFaLrsvqosOG/oBYG0sM1MhZv" +
            "wxNCQ1FSXnzVu7oQH2aMRsCcKBo62ONztG2daxTPCIDq+FdVJgYOx4HtpBzt8ZrFUYp8pmBbDUhi" +
            "sScCgYBnJlSzhW3y10d6kEXFOEeYX29HOpC0BLMy+EWHOMeXyRo751ivrUbnUVqFncOK0wTn0FRz" +
            "02GCyAW9cy6wetiMSQ+nL6zArI13O0NJOyfUy/gucUl8a43i2BoYFfaoWFEu/f2pF3Q8vZSHIrMa" +
            "ZRDu6MGQ0/o3qq8ZfgzIFcZypg==";

    /**
     * 校验token是否正确
     *
     * @param token 密钥
     * @return
     */
    public static boolean checkAccessToken(String token, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ResponseResult resultUtil) {
        try {
            //解密tokenId
            byte[] tokenBytes = RSAUtil.privateDecrypt(RSAUtil.base642Byte(token), RSAUtil.string2PrivateKey(privateKey));
            String tokenStr = new String(tokenBytes);
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(tokenStr);
            long expireTime=jwt.getClaim("expireTime").asLong();
            if(expireTime <= System.currentTimeMillis()){
                //已经超时
                outResult(httpServletResponse,  ResponseResults.fail(ResultEnum.ACCESS_TOKEN_TIMEOUT));
                return false;
            }
            String userId = jwt.getClaim("userId").asString();
            String userName = jwt.getClaim("userName").asString();
            httpServletRequest.setAttribute("userId", userId);
            httpServletRequest.setAttribute("userName", userName);
            return true;
        } catch (Exception e) {
            if (e instanceof TokenExpiredException) {
                System.err.println("超时错误："+e);
                outResult(httpServletResponse, ResponseResults.fail(ResultEnum.ACCESS_TOKEN_TIMEOUT));
                return false;
            }
            System.err.println("错误：");
            System.out.println(e);
            outResult(httpServletResponse, ResponseResults.fail(ResultEnum.ACCESS_TOKEN_TIMEOUT));
            return false;
        }
    }

    public static boolean outResult(HttpServletResponse response, ResponseResult resultUtil) {
        PrintWriter out = null;
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=utf-8");
            out = response.getWriter();
        } catch (IOException e) {
            e.printStackTrace();
        }
        out.write(JSON.toJSONString(resultUtil));
        return false;
    }

    /**
     * 获取登录用户ID
     * @param request
     * @return
     */
    public static String getUserId(HttpServletRequest request) throws Exception{
        String accessToken = request.getHeader("accessToken");
        byte[] tokenBytes = RSAUtil.privateDecrypt(RSAUtil.base642Byte(accessToken), RSAUtil.string2PrivateKey(privateKey));
        String tokenStr = new String(tokenBytes);
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(tokenStr);
        String userId = jwt.getClaim("userId").asString();
        return userId;
    }

    /**
     * 生成签名
     * @param userId 用户ID
     * @param userName 用户名
     * @param deviceId 设备ID
     * @param expireTime accessToken过期时间（单位：毫秒）
     * @param delayTime accessToken的过期延长时间，加上expireTime为refreshToken的过期时间（单位：毫秒）
     * @param resultUtil 返回结果
     * @return
     */
    public static ResponseResult getkAccessToken(String userId,String userName, String deviceId, long expireTime, long delayTime, ResponseResult resultUtil) {
        try {
            // 当前日期
            long millis = System.currentTimeMillis();
            Date nowDate = new Date(millis);

            // accessToken过期时间
            long date = millis + expireTime;
            // refreshToken过期时间
            Long refreshTokenExpiresAtDate = millis + expireTime + delayTime;
            Date expiresDate = new Date(refreshTokenExpiresAtDate);

            Map<String, Object> map = new HashMap<String, Object>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            String token = JWT.create().withHeader(map)    //请求头
                    .withClaim("iss", "Service")    //签发方
                    .withClaim("aud", "Client")        //接收方
                    .withClaim("userId", userId) //存储信息，用户ID
                    .withClaim("userName", userName) //用户名称
                    .withClaim("expireTime", date)
                    .withIssuedAt(nowDate)        //当前时间
                    .withExpiresAt(expiresDate)        //过期时间
                    .sign(Algorithm.HMAC256(SECRET));        //私钥
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("userId", userId);
            // 设备编号
            jsonObject.put("deviceId", deviceId);
            PublicKey publicKey1 = RSAUtil.string2PublicKey(publickey);
            jsonObject.put("refreshTokenExpiresAtDate", refreshTokenExpiresAtDate);
            jsonObject.put("expireTime", expireTime);
            jsonObject.put("delayTime", delayTime);
            // 加密refreshToken
            byte[] bytes = RSAUtil.publicEncrypt(JSON.toJSONString(jsonObject).getBytes(), publicKey1);
            String refreshTokenStr = Base64.encodeBase64String(bytes);
            //String refreshTokenStr = RSAUtil.byte2Base64(bytes);
            // 加密accessToken
            byte[] accessTokenBytes = RSAUtil.publicEncrypt(token.getBytes(), publicKey1);
//            String accessTokenStr = RSAUtil.byte2Base64(accessTokenBytes);
            // 解决出现换行符
            String accessTokenStr = Base64.encodeBase64String(accessTokenBytes);
            Map<String, Object> tokenMap = new HashMap<>();
            tokenMap.put("accessToken", accessTokenStr);
            tokenMap.put("refreshToken", refreshTokenStr);
            tokenMap.put("expireTime", expireTime);
            tokenMap.put("delayTime", delayTime);
            return ResponseResults.success(ResultEnum.SUCCESS,"登录成功");
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof InvalidKeySpecException) {
                System.err.println("加密错误：" + e);
                return ResponseResults.fail(ResultEnum.SERVER_ERR);

            }
             return ResponseResults.fail(ResultEnum.SERVER_ERR);
        }
    }

    /**
     * 刷新refresh_token
     *
     * @return
     */

    public static ResponseResult refreshToken(String refreshToken, ResponseResult resultUtil) {
        try {
            //通过refreshToken获取相关信息
            //解决出现换行符
            byte[] refreshTokennBytes = RSAUtil.privateDecrypt(RSAUtil.base642Byte(refreshToken), RSAUtil.string2PrivateKey(privateKey));
            String refreshTokenStr = new String(refreshTokennBytes);
            JSONObject jsonObject = JSONObject.parseObject(refreshTokenStr);
            String userId = jsonObject.getString("userId");
            String userName = jsonObject.getString("userName");
            String deviceId = jsonObject.getString("deviceId");
            long refreshTokenExpiresAtDate = jsonObject.getLong("refreshTokenExpiresAtDate");
            long expireTime = jsonObject.getLong("expireTime");
            long delayTime = jsonObject.getLong("delayTime");
            //给2分钟时间差
            if (refreshTokenExpiresAtDate + 2 * 60 * 1000 >= System.currentTimeMillis()) {
                return getkAccessToken(userId,userName, deviceId, expireTime, delayTime, resultUtil);
            }
            return  ResponseResults.fail(ResultEnum.REFRESH_TOKEN_TIMEOUT);
        } catch (Exception e) {
            if (e instanceof InvalidKeySpecException) {
                System.err.println("加密错误：" + e);
                return ResponseResults.fail(ResultEnum.SERVER_ERR);
            }
            return ResponseResults.fail(ResultEnum.SERVER_ERR);
        }
    }



    public static void main(String[] args) throws Exception {
        String token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJDbGllbnQiLCJpc3MiOiJTZXJ2aWNlIiwidXNlck5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJleHAiOjE1NzkxNDYyODUsInVzZXJJZCI6ImUxNjljMTNjMzZhMzExZWFiZmJhZDcwNzljZmExMTYyIiwiaWF0IjoxNTc5MTM5MDg1fQ.XjunAvSFeqWLpXJTqFi4Cl9-F4z8m9SJz5OCYDzEPSw";
        PublicKey publicKey1 = RSAUtil.string2PublicKey(publickey);
        //加密accessToken
        byte[] accessTokenBytes = RSAUtil.publicEncrypt(token.getBytes(), publicKey1);
        //解决出现换行符
        String accessTokenStr = Base64.encodeBase64String(accessTokenBytes);
        System.err.println(accessTokenStr);

        //解密tokenId
        byte[] tokenBytes = RSAUtil.privateDecrypt(RSAUtil.base642Byte(accessTokenStr), RSAUtil.string2PrivateKey(privateKey));
        String tokenStr = new String(tokenBytes);
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(tokenStr);
        String userId = jwt.getClaim("userId").asString();
        System.err.println(userId);
    }
}

